use std::{
    collections::BTreeSet, fs, io::Write,
};
use crate::{
    defines::*, game::*, utils::{normalize, ByteRead,ByteWrite,ByteReader,ByteWriter},
};

const WHOLE_LOC_MAP:[usize;BOARD_LEN] = [
    0,  1,  2,  3,  4,  4,  3,  2,  1,  0,
    1,  5,  6,  7,  8,  8,  7,  6,  5,  1,
    2,  6,  9, 10, 11, 11, 10,  9,  6,  2,
    3,  7, 10, 12, 13, 13, 12, 10,  7,  3,
    4,  8, 11, 13, 14, 14, 13, 11,  8,  4,
    4,  8, 11, 13, 14, 14, 13, 11,  8,  4,
    3,  7, 10, 12, 13, 13, 12, 10,  7,  3,
    2,  6,  9, 10, 11, 11, 10,  9,  6,  2,
    1,  5,  6,  7,  8,  8,  7,  6,  5,  1,
    0,  1,  2,  3,  4,  4,  3,  2,  1,  0,
];

const DEFAULT_LWS:[[usize;BOARD_LEN];2] = [
    [3881657, 4047263, 4180143, 4251981, 4294788, 4299199, 4286103, 4229282, 4110881, 4009109, 4046559, 3711753, 3497008, 3537741, 3550197, 3565464, 3548756, 3528787, 3782717, 4109165, 4181456, 3498496, 3599983, 3424926, 3473984, 3468691, 3442510, 3631153, 3529981, 4228955, 4252988, 3540397, 3424927, 3627658, 3483611, 3483989, 3637598, 3440396, 3548516, 4284405, 4295481, 3550554, 3473950, 3482048, 3676608, 3674565, 3479310, 3470084, 3562293, 4298603, 4297794, 3561098, 3468999, 3482774, 3675912, 3672738, 3481280, 3471366, 3549628, 4296793, 4283864, 3547325, 3440460, 3639939, 3483172, 3482468, 3627621, 3424372, 3536829, 4254364, 4226972, 3528495, 3631155, 3438526, 3468403, 3474898, 3426574, 3598092, 3497071, 4180632, 4108406, 3782905, 3527651, 3547503, 3562320, 3550193, 3537180, 3498274, 3712348, 4048359, 4008895, 4106971, 4226224, 4283227, 4298981, 4295034, 4251510, 4178025, 4046025, 3881626],
    [5020142, 5312422, 5455059, 5529465, 5553455, 5561804, 5520028, 5458463, 5323999, 5037739, 5311705, 5189058, 4745572, 4702814, 4698984, 4694464, 4707836, 4749767, 5182256, 5321661, 5452094, 4745146, 4810984, 4519248, 4519976, 4524006, 4517523, 4803493, 4748746, 5458892, 5526531, 4702237, 4518869, 4729426, 4508567, 4512774, 4708877, 4517603, 4706797, 5520339, 5551384, 4698911, 4517602, 4511781, 4749625, 4721522, 4514865, 4524992, 4691977, 5562657, 5559727, 4695042, 4526661, 4513932, 4723568, 4751319, 4510743, 4520125, 4696390, 5553356, 5517511, 4708899, 4515959, 4709026, 4516543, 4509107, 4728703, 4518131, 4698159, 5529273, 5456691, 4749342, 4805515, 4518150, 4527708, 4517942, 4520458, 4811605, 4742140, 5456561, 5322138, 5179996, 4747758, 4707982, 4694316, 4697318, 4701588, 4742549, 5189079, 5315940, 5039475, 5323134, 5459157, 5519805, 5563491, 5551384, 5528590, 5455669, 5314355, 5021153],
];
const DEFAULT_TIMES:usize = 15461515;
const DEFAULT_NW_BLACK:usize = 6584763;
const DEFAULT_NW_WHITE:usize = 8430300;

type LWS = [usize; BOARD_LEN];
fn make_table(lw:&LWS, n:usize) -> [f32; BOARD_LEN] {
    let mut avg_wins:[f32; 15] = [0f32; 15];
    let n = n as f32;
    for i in 0..lw.len() {
        let ai = WHOLE_LOC_MAP[i];
        let loc_wins = lw[i] as f32;
        if loc_wins > 0.0 {
            avg_wins[ai] += loc_wins / n;
            avg_wins[ai] /= 2.0;
        }
    }
    let mut table:[f32; BOARD_LEN] = [0f32; BOARD_LEN];
    for i in 0..BOARD_LEN {
        table[i] = avg_wins[WHOLE_LOC_MAP[i]];
    }
    let nor = normalize(&table);
    for i in 0..BOARD_LEN {
        table[i] = nor[i];
    }
    table
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Line {
    bits_piece: u128,
    bits_color: u128,
}
impl Line {
    fn new (board:&Board) -> Self {
        Self {
            bits_piece: board.bits_piece, bits_color: board.bits_color,
        }
    }
}
impl ByteRead for Line {
    fn from_bytereader(reader:&mut ByteReader) -> Result<Self> where Self: Sized {
        if reader.remsize() >= size_of::<Self>() {
            Ok(Self {
                bits_piece: u128::from_bytereader(reader)?,
                bits_color: u128::from_bytereader(reader)?,
            })
        } else {
            Err(anyhow!("remain({}) bytes incompose, need {}", reader.remsize(), size_of::<Self>()))
        }
    }
}
impl ByteWrite for Line {
    fn to_bytewriter(&self, writer:&mut ByteWriter) -> Result<()> {
        self.bits_piece.to_bytewriter(writer)?;
        self.bits_color.to_bytewriter(writer)?;
        Ok(())
    }
}

#[repr(u32)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum WholeStatus {
    Final = 0,
    Learnable = 1,
    Incorrent = 2,
}
impl WholeStatus {
    pub fn is_final(&self) -> bool {
        match *self {
            WholeStatus::Final => {true}
            _ => {false}
        }
    }
    pub fn learn_able(&self) -> bool {
        match *self {
            WholeStatus::Learnable => {true}
            _ => {false}
        }
    }
}
impl ByteRead for WholeStatus {
    fn from_bytereader(reader:&mut ByteReader) -> Result<Self> where Self: Sized {
        let code = u8::from_bytereader(reader)?;
        match code {
            b'F' => {Ok(WholeStatus::Final)}
            b'L' => {Ok(WholeStatus::Learnable)}
            _ => {Ok(WholeStatus::Incorrent)}
        }
    }
}
impl ByteWrite for WholeStatus {
    fn to_bytewriter(&self, writer:&mut ByteWriter) -> Result<()> {
        let code = match *self {
            WholeStatus::Final => {b'F'}
            WholeStatus::Learnable => {b'L'}
            _ => {0u8}
        };
        code.to_bytewriter(writer)
    }
}
impl std::fmt::Display for WholeStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let text = match *self {
            WholeStatus::Final => {"Final"}
            WholeStatus::Learnable => {"Learnable"}
            WholeStatus::Incorrent => {"Incorrent"}
        };
        write!(f, "{}", text)
    }
}

pub struct WholeFrame {
    status: WholeStatus,
    lw_blacks: LWS,
    lw_whites: LWS,
    times: usize,
    nw_black: usize,
    nw_white: usize,
    lines: BTreeSet<Line>,
}
impl WholeFrame {
    pub fn new() -> Self {
        Self {
            status: WholeStatus::Learnable,
            lw_blacks: [0;BOARD_LEN], lw_whites: [0;BOARD_LEN],
            times: 0, nw_black: 0, nw_white: 0,
            lines: BTreeSet::new(),
        }
    }
    pub fn load(path:&str) -> Result<Self> {
        let bytes = fs::read(path)?;
        let mut reader = ByteReader::from(&bytes);
        Self::from_bytereader(&mut reader)
    }
    pub fn save(&self, path:&str) -> Result<()> {
        let mut file = fs::OpenOptions::new().write(true).create(true).open(path)?;
        let mut writer = ByteWriter::new();
        self.to_bytewriter(&mut writer)?;
        file.write(writer.bytes())?;
        file.flush()?;
        Ok(())
    }
    pub fn status(&self) -> WholeStatus {self.status}
    pub fn line_count(&self) -> usize {self.lines.len()}
    pub fn times(&self) -> usize {self.times}
    pub fn nw_black(&self) -> usize {self.nw_black}
    pub fn nw_white(&self) -> usize {self.nw_white}
    pub fn table_black(&self) -> [f32;BOARD_LEN] {make_table(&self.lw_blacks, self.times())}
    pub fn table_white(&self) -> [f32;BOARD_LEN] {make_table(&self.lw_whites, self.times())}
    pub fn lw_blacks(&self) -> LWS {self.lw_blacks}
    pub fn lw_whites(&self) -> LWS {self.lw_whites}
    pub fn to_final(&mut self) {
        self.status = WholeStatus::Final;
    }
    fn add(&mut self, board:&Board) -> bool {
        let line = Line::new(board);
        if self.lines.insert(line) {
            let pieces_diff = board.n_white() as i32 - board.n_black() as i32;
            if pieces_diff < 0 {
                self.nw_black += 1;
                for i in 0..BOARD_LEN {
                    if Piece::Black == board.get(i as Loc) {
                        self.lw_blacks[i] += 1;
                    }
                }
            } else if pieces_diff > 0 {
                self.nw_white += 1;
                for i in 0..BOARD_LEN {
                    if Piece::White == board.get(i as Loc) {
                        self.lw_whites[i] += 1;
                    }
                }
            }
            true
        } else {
            false
        }
    }
    pub fn frame(&mut self) {
        debug_assert!(self.status.learn_able());
        let board = Board::make_random_board_by_init(BOARD_LEN, BOARD_LEN);
        self.times += self.add(&board) as usize;
    }
}
impl Default for WholeFrame {
    fn default() -> Self {
        Self {
            status: WholeStatus::Final,
            lw_blacks: DEFAULT_LWS[0], lw_whites: DEFAULT_LWS[1],
            times: DEFAULT_TIMES, nw_black: DEFAULT_NW_BLACK, nw_white: DEFAULT_NW_WHITE,
            lines: BTreeSet::new(),
        }
    }
}
const BIN_MINSIZE:usize = 813;
impl ByteRead for WholeFrame {
    fn from_bytereader(reader:&mut ByteReader) -> Result<Self> where Self: Sized {
        if reader.remsize() < BIN_MINSIZE {
            return Err(anyhow!("lessthen min-size:{}", BIN_MINSIZE))
        }
        fn read_lws(reader:&mut ByteReader) -> [usize;BOARD_LEN] {
            let mut lws = [0usize; BOARD_LEN];
            for i in 0..BOARD_LEN {
                let a = u32::from_bytereader(reader).unwrap();
                lws[i] = a as usize;
            }
            lws
        }
        let status = WholeStatus::from_bytereader(reader)?;
        let lw_blacks = read_lws(reader);
        let lw_whites = read_lws(reader);
        let times = u32::from_bytereader(reader)? as usize;
        let nw_black = u32::from_bytereader(reader)? as usize;
        let nw_white = u32::from_bytereader(reader)? as usize;
        let mut lines = BTreeSet::new();
        if status.learn_able() {
            let line_count = u32::from_bytereader(reader)? as usize;
            for _ in 0..line_count {
                lines.insert(Line::from_bytereader(reader)?);
            }
        }
        Ok(Self {status, lw_blacks, lw_whites, times, nw_black, nw_white, lines})
    }
}
impl ByteWrite for WholeFrame {
    fn to_bytewriter(&self, writer:&mut ByteWriter) -> Result<()> {
        fn write_lws(lws:&[usize;BOARD_LEN], writer:&mut ByteWriter) -> Result<()> {
            for i in 0..BOARD_LEN {{
                let a = lws[i] as u32;
                a.to_bytewriter(writer)?;
            }}
            Ok(())
        }
        self.status.to_bytewriter(writer)?;
        write_lws(&self.lw_blacks, writer)?;
        write_lws(&self.lw_whites, writer)?;
        (self.times as u32).to_bytewriter(writer)?;
        (self.nw_black as u32).to_bytewriter(writer)?;
        (self.nw_white as u32).to_bytewriter(writer)?;
        if self.status.learn_able() {
            (self.line_count() as u32).to_bytewriter(writer)?;
            for a in &self.lines {
                a.to_bytewriter(writer)?;
            }
        }
        Ok(())
    }
}

struct RolloutSide {

}
impl Default for RolloutSide {
    fn default() -> Self {
        Self {}
    }
}
pub struct Rollout {
    sides: [RolloutSide;2],
}
impl Rollout {
    pub fn new() -> Self {
        Self {sides: Default::default()}
    }
    pub fn fuck(&self, moves:&[Loc], pcur:Piece) -> Loc {
        let side = &self.sides[pcur as usize - 1];
        moves[0]
    }
    pub fn frame(&mut self) {
    }
}
